android 窗口管理机制

您所在的位置:网站首页 android 层级banner android 窗口管理机制

android 窗口管理机制

2023-09-11 19:02| 来源: 网络整理| 查看: 265

1、简介

Android的窗口管理主要由Window, WindowManager, WindowManagerServic三者实现window是一个抽象的概念,它必须为其添加一个View实体才能被我们所看到,不管是Activity,Toast,还是其他的Dialog之类的组件,都必须放在window上。而如果我们要对window进行操作的话,就必须通过windowManager,事实上,对window的操作是通过WindowManagerServic来实现的,而WindowManager可以看做是一个管理者,而不是一个实施者,它提供各种方法和指令来操作window,但是这些操作指令都需要通过IPC传递给WindowManagerServic来具体实现。

2、Window 2.1 窗口类型

window有三种类型,从低到高分别为子window,应用window,系统window,每个window有着对应的层级(z-ordered), 层级大的可以覆盖在层级小的window上面,不同类型的window的层级范围如图所示

类型层级范围子window0-99应用window1000-1999系统window2000-2999

在WindowManager.LayoutParams.TYPE_***中,我们可以看到多种不同的类型以及它们对应的值,系统运行期间,很有可能多个窗口属于同一个类型。因此WMS需要根据实际情况来调整他们的层级值。系统类的window创建必须要在AndroidManifest,xml中声明权限后才能创建。

2.2 窗口属性

通过设置不同的窗口属性,我们可以使UI窗口显示出不同的样式,它们就行窗口用户和WMS之间的协议一样,WMS负责实现用户端声明的这些属性。这些属性都放在了WindowManager.LayoutParams中,这里列举两个。

// 只要这个window对用户是可见的,则允许在屏幕开启的时候锁定屏幕 public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; // 窗口后面的所有东西都变暗 public static final int FLAG_DIM_BEHIND = 0x00000002; // 此窗口不获得输入焦点,事件将分发给窗口后面的其他窗口 public static final int FLAG_BLUR_BEHIND = 0x00000004; ......

比如有时候希望全屏显示,取消statusBar这些组件,就可以使用

Window w = this.getWindow(); w.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

window可以看做是一个容器,但它其实是一个抽象的概念,Activity,Dialog以及它们的视图都必须放在window上才能显现, 每个window对应着一个View和一个ViewRootImpl, 视图View负责定义window的布局以及样式等,而 ViewRootImpl则是负责View的渲染。window和View是通过ViewRootImpl来建立起联系的。

3、WindowManager

WindowManager是联系window和WindowManagerService的桥梁,是Android窗口管理机制的枢纽。它继承子ViewManager,定义了对于window的基本操作。

public interface WindowManager extends ViewManager{ ............. } public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }

这样看起来就清楚多了,它其实主要就是对外提供了对View的增加,更新,删除操作。所以我们也可以直接通过WindowManager来对window实现这些操作。

@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.mainactivity); // 添加一个文本框 TextView textView=new TextView(this); textView.setText("Window text"); // 设置布局参数 WindowManager.LayoutParams layoutParams=new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0,0, PixelFormat.TRANSPARENT ); // 设置window的属性 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; // 设置window的层级 layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; layoutParams.gravity = Gravity.CENTER; WindowManager windowManager = getWindowManager(); // 添加View windowManager.addView(textView,layoutParams); }

这里我们添加了一个文本框,并且设置它的层级的TYPE_APPLICATION_OVERLAY,表示可以浮在其他应用的上面,具体值是2038,即一个系统window。除了要声明权限之外,我们还要在应用/高级设置中开启它可以浮在其他应用上面的权限才行。

在这里插入图片描述 最后,可以发现这个textview可以显示在应用中,也可以直接显示在桌面上。这里说明,即便我们退出了MainActivity之后,这个textview依然存在,即textview真正依附的是window而不是Activity,对于窗口显示来说,Activity其实不是必须的,比如经常使用的Toast,它就没有对应的Activity。 在这里插入图片描述

那么Activity是如何创建window,以及绑定对应的windowManager的呢?下面我们来探究一下。在Activity调用onCreate之前,它会先调用attach方法

final void attach(Context context, ActivityThread aThread, ...... mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(mWindowControllerCallback); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } ...... mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; mWindow.setColorMode(info.colorMode); mWindow.setPreferMinimalPostProcessing( (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0); setAutofillOptions(application.getAutofillOptions()); setContentCaptureOptions(application.getContentCaptureOptions()); }

在这个方法中我们可以看到,window的具体实现类是PhoneWindow,将其实例化之后传递给Activity的成员mWindow,之后设置好它的回调函数。在方法mWindow.setWindowManager()中,将window和windowManager绑定在一起。下面看一下setWindowManager()方法

public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { ... if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }

这里可以看到,如果wm是空的话,那么为其赋值,这里其实是获取系统服务 WindowManagerService,然后在调用createLocalWindowManager来创建一个真正的windowManager代理,再进入createLocalWindowManager方法中

public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }

发现它其实是返回了一个WindowManagerImp的实例。综上,attach方法其实完成的主要工作就是创建了Activity的window对象和windowManager对象,而将Activity对应的View附属到window中,其实是在onCreate方法中完成的,即 setContentView()方法。

public void setContentView(int layoutResID){ getWindow().setContentView(layoutResID); ... }

这里通过getWindow获取之前创建的PhoneWindow对象,然后再调用setContentView将window与View绑定在一起。

在Activity调用onResume方法之后,会调用makeVisible()方法,将window加入到windowManager中,那么windowManager就可以对window进行管理了。

void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }

之前我们说道过,window是抽象的,必须通过windowManager对其进行访问,windowManager是继承自viewmanager的,它提供了对视图的增,删,改操作。而视图是与window绑定在一起的,所以对View的操作其实也是对window的操作。 上面代码中可以看出,windowManager的具体实现类其实是WindowManagerImpl,看一下它的源码

@Override public void addView(View view, ViewGroup.LayoutParams params){ ... mGlobal.addView(view, params, mDisplay, mParentWindow); } @Override public void updateViewLayout(View view, ViewGroup.LayoutParams params){ ... mGlobal.updateViewLayout(view, params); } @Override public void removeView(View view){ ... mGlobal.removeView(view, false); }

它把对视图的操作都交给了成员mGlobal去实现,mGlobal是一个WindowManagerGlobal类,再看一下它的源码,mGlobal中提供了对视图进行增、删、改的操作函数,同时还用了几个集合来保存和window相关的信息,具体如下。

public final class WindowManagerGlobal{ // 存储所有window对应的View private final ArrayList mViews = new ArrayList(); // 存储所有window对应的ViewRootImpl private final ArrayList mRoots = new ArrayList(); // 存储所有的window对应的布局参数 private final ArrayList mParams =new ArrayList(); // 存储已经调用removeView方法但是操作删除还未完成的对象 private final ArraySet mDyingViews = new ArraySet(); public void addView(...){ ... } public void updateViewLayout(...){ ... } public void removeView(...){ ... } ... }

综上,WindowManager到WindowManagerService的调用路径为 在这里插入图片描述

4、WindowManagerService

WindowManagerService是实际控制window的显示以及渲染等操作的,它的实现比较复杂,由SystemService负责启动的,它就像一个导演一样,和surfaceFlinger一起负责界面的显示工作,全局的事件派发工作,只有在关机的时候才能退出。

4.1 服务启动

和AMS等服务一样,WMS也是系统服务的一部分,它由SystemServer负责启动,并加到服务列表中,WMS提供了一个静态的main方法来实现真正的创建工作。

// SystemServer.java wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore, new PhoneWindowManager()); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); public static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy) { // 创建真正的WMS对象 DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore, policy), 0); return sInstance; } 4.2 基本功能

在文件IWindowManager.aidl中,我们可以看到WindowManagerService可以提供哪些功能

...... void getBaseDisplaySize(int displayId, out Point size); void setForcedDisplaySize(int displayId, int width, int height); void clearForcedDisplaySize(int displayId); @UnsupportedAppUsage int getInitialDisplayDensity(int displayId); int getBaseDisplayDensity(int displayId); void setForcedDisplayDensityForUser(int displayId, int density, int userId); void clearForcedDisplayDensityForUser(int displayId, int userId); void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable void setOverscan(int displayId, int left, int top, int right, int bottom); // These can only be called when holding the MANAGE_APP_TOKENS permission. void setEventDispatching(boolean enabled); void addWindowToken(IBinder token, int type, int displayId); void removeWindowToken(IBinder token, int displayId); void setFocusedApp(IBinder token, boolean moveFocusNow); void prepareAppTransition(int transit, boolean alwaysKeepCurrent); ......

从这个文件中可以看到,WMS的功能庞杂繁多,比如获取显示器的信息,截取屏幕,锁定屏幕等各种功能。

4.3 工作方式

WMS的工作线程是SystemServer中的wmHandlerThread, 这样的设计使得SystemServer中的其他服务可以直接将事件投递到wmHandlerThread线程中,从而WMS可以直接从线程中获取事件进行处理,此外,服务请求也可以直接通过Binder机制调用WMS进行服务,所以调用者可以通过直接或者间接的方式调用到WMS服务。

事件投递直接调用

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3